iT邦幫忙

2026 iThome 鐵人賽

DAY 6
0
AI 自動化

AaaS from Scratch: 從一次性定義,到規模化分析系列 第 6

[Day 6] 從 Config 開始建立 Datasource System

  • 分享至 

  • xImage
  •  

接下來要建立 datasource system,讓 Agent 未來可以使用不同類型的資料來源。

跟這個系列前面的內容一樣,這裡沒有唯一正確的做法。比較像是我自己的設計習慣,以及我希望這個 system 最後可以長成什麼樣子。

對我來說,我比較喜歡 start from the end:先想清楚最後希望 system 怎麼被使用,再一步一步往那個方向做。

例如:

  • 如果之後要支援 PDF,理想上只需要補上 PDF reader 和相關設定。
  • 如果要支援其他 file format,Agent 本身不需要跟著修改。
  • 如果要連到另一個 PostgreSQL database,理想上只需要新增 connection configuration。
  • 如果要支援另一種 database,再補上對應的處理邏輯。

核心想法很簡單:

新增 datasource 時,盡量少改動既有的 system。

不過在開始寫 CSV reader 之前,我想先建立整個 application 之後都會使用的 config layer。

目前 datasource 是第一個需要設定的部分,所以先從它開始。

Config

我希望目前的 config 先保持很簡單:

data:
  path: data/youtube_trending_cleaned_us.csv

之後如果 system 增加其他需要設定的部分,也可以繼續加到同一份 config 裡。

例如未來可能會有:

data:
  ...

database:
  ...

sandbox:
  ...

但目前還沒有需要,所以先不加。

另外,這裡不會另外設定 format

file format 直接從 path 的 extension 判斷,避免同一件事情出現兩個 source of truth。

Prompt

這是我給 coding agent 的 prompt:

Add a config layer.

config.yml at the repo root, one section per concern. For now just:

  data:
    path: data/youtube_trending_cleaned_us.csv

A pydantic schema under config/, split by section:
  - config/data.py — DataType enum (CSV only) and DataConfig
  - config/base.py — the top-level Config, one field per section,
    with a load() classmethod that reads the yml and validates it
  - config/__init__.py — cfg = Config.load(), which the app imports

The data format is not a config field — it follows from the path's
extension. The enum exists so code can name a format without a magic
string. An unsupported extension must fail when the config loads, not
when something first reads the file.

Reject unknown keys.

Keep it minimal — no settings the app doesn't read, no options nothing
uses, no abstraction for a single case.

Test that the yml parses into the schema, that an unknown format is
rejected at load time, and that format can't be set in the yml.

Project Structure

目前 config 相關的結構如下:

config.yml

config/
├── __init__.py
├── base.py
└── data.py

config/base.py 負責整份 application config。

config/data.py 則只處理 datasource 相關設定。

目前只有一個 section:data

config.yml

repo root 的設定如下:

data:
  path: data/youtube_trending_cleaned_us.csv

目前只需要提供 data path。

config/data.py

先定義 application 目前支援的 data format:

from enum import Enum
from pathlib import Path

from pydantic import BaseModel, ConfigDict, model_validator


class DataType(str, Enum):
    CSV = "csv"

    @classmethod
    def from_path(cls, path: Path) -> "DataType":
        suffix = path.suffix.lower().lstrip(".")

        try:
            return cls(suffix)
        except ValueError:
            raise ValueError(
                f"unsupported data format {suffix!r} for {path}"
            ) from None

例如:

DataType.from_path(Path("data.csv"))

會得到:

DataType.CSV

如果是目前不支援的格式,例如:

DataType.from_path(Path("data.pdf"))

則會直接失敗。

接著是 DataConfig

class DataConfig(BaseModel):
    model_config = ConfigDict(extra="forbid")

    path: Path

    @model_validator(mode="after")
    def _check_format(self) -> "DataConfig":
        DataType.from_path(self.path)
        return self

    @property
    def format(self) -> DataType:
        return DataType.from_path(self.path)

這裡主要做兩件事:

  • config load 時就確認 format 是否支援
  • reject unknown fields

config/base.py

接著是 top-level config:

from pathlib import Path

import yaml
from pydantic import BaseModel, ConfigDict

from .data import DataConfig


class Config(BaseModel):
    model_config = ConfigDict(extra="forbid")

    data: DataConfig

    @classmethod
    def load(cls, path: Path | str = "config.yml") -> "Config":
        return cls.model_validate(
            yaml.safe_load(Path(path).read_text())
        )

這裡負責讀取整份 config.yml,並在 application 啟動時一起完成 validation。

之後如果有新的 config section,也可以繼續加到這裡。

config/__init__.py

最後:

from .base import Config

cfg = Config.load()

application 其他地方就可以直接使用:

from config import cfg

print(cfg.data.path)
print(cfg.data.format)

這樣 config 的讀取與 validation 都集中在同一個地方,不需要每個 module 自己處理。

到這裡,我們還沒有真的開始寫 CSV reader。

但 application 已經有一個可以繼續擴充的 config layer,而且 datasource 也有了最基本的設定。

下一步就可以直接使用:

cfg.data.path
cfg.data.format

開始建立真正的 file reader。


上一篇
[Day 5] 設定開發環境,Coding Agent跟Skills
下一篇
[Day 7] 實作Datasource,並提供給Agent使用
系列文
AaaS from Scratch: 從一次性定義,到規模化分析9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言